package eva

import (
	"testing"

	"gitcode.com/deyiyangyang/gascheme/lex"
	"gitcode.com/deyiyangyang/gascheme/obj"
	"gitcode.com/deyiyangyang/gascheme/par"
)

// TestIfTrueBranch tests if expressions where the test is true
func TestIfTrueBranch(t *testing.T) {
	// Create an if expression: (if #t 1 2)
	input := "(if #t 1 2)"

	// Parse the expression
	l := lex.NewLexer(input)
	p := par.NewParser(l)
	expr, err := p.ParseExpression()
	if err != nil {
		t.Fatalf("Parse error: %s", err)
	}

	// Create environment and register built-in procedures
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)

	// Evaluate the expression
	result := Eval(expr, env)

	// Check if the result is an Integer object with value 1
	integer, ok := result.(*obj.Integer)
	if !ok {
		t.Fatalf("Eval didn't return Integer. Got %T", result)
	}

	if integer.Value != 1 {
		t.Errorf("Eval returned wrong value. Got %d, want 1", integer.Value)
	}
}

// TestIfFalseBranch tests if expressions where the test is false
func TestIfFalseBranch(t *testing.T) {
	// Create an if expression: (if #f 1 2)
	input := "(if #f 1 2)"

	// Parse the expression
	l := lex.NewLexer(input)
	p := par.NewParser(l)
	expr, err := p.ParseExpression()
	if err != nil {
		t.Fatalf("Parse error: %s", err)
	}

	// Create environment and register built-in procedures
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)

	// Evaluate the expression
	result := Eval(expr, env)

	// Check if the result is an Integer object with value 2
	integer, ok := result.(*obj.Integer)
	if !ok {
		t.Fatalf("Eval didn't return Integer. Got %T", result)
	}

	if integer.Value != 2 {
		t.Errorf("Eval returned wrong value. Got %d, want 2", integer.Value)
	}
}

// TestIfWithoutAlternate tests if expressions without an alternate branch
func TestIfWithoutAlternate(t *testing.T) {
	// Create an if expression: (if #t 1)
	input := "(if #t 1)"

	// Parse the expression
	l := lex.NewLexer(input)
	p := par.NewParser(l)
	expr, err := p.ParseExpression()
	if err != nil {
		t.Fatalf("Parse error: %s", err)
	}

	// Create environment and register built-in procedures
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)

	// Evaluate the expression
	result := Eval(expr, env)

	// Check if the result is an Integer object with value 1
	integer, ok := result.(*obj.Integer)
	if !ok {
		t.Fatalf("Eval didn't return Integer. Got %T", result)
	}

	if integer.Value != 1 {
		t.Errorf("Eval returned wrong value. Got %d, want 1", integer.Value)
	}
}

// TestIfWithoutAlternateAndFalse tests if expressions without an alternate branch and test is false
func TestIfWithoutAlternateAndFalse(t *testing.T) {
	// Create an if expression: (if #f 1)
	input := "(if #f 1)"

	// Parse the expression
	l := lex.NewLexer(input)
	p := par.NewParser(l)
	expr, err := p.ParseExpression()
	if err != nil {
		t.Fatalf("Parse error: %s", err)
	}

	// Create environment and register built-in procedures
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)

	// Evaluate the expression
	result := Eval(expr, env)

	// Check if the result is nil
	if result != nil {
		t.Errorf("Eval returned wrong value. Got %v, want nil", result)
	}
}

// TestIfWithComplexExpressions tests if expressions with complex test, consequent, and alternate expressions
func TestIfWithComplexExpressions(t *testing.T) {
	// Create an if expression: (if (> 5 3) (+ 1 2) (- 1 2))
	input := "(if (> 5 3) (+ 1 2) (- 1 2))"

	// Parse the expression
	l := lex.NewLexer(input)
	p := par.NewParser(l)
	expr, err := p.ParseExpression()
	if err != nil {
		t.Fatalf("Parse error: %s", err)
	}

	// Create environment and register built-in procedures
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)

	// Evaluate the expression
	result := Eval(expr, env)

	// Check if the result is an Integer object with value 3
	integer, ok := result.(*obj.Integer)
	if !ok {
		t.Fatalf("Eval didn't return Integer. Got %T", result)
	}

	if integer.Value != 3 {
		t.Errorf("Eval returned wrong value. Got %d, want 3", integer.Value)
	}
}

// TestIfWithComplexExpressionsAndFalse tests if expressions with complex expressions where test is false
func TestIfWithComplexExpressionsAndFalse(t *testing.T) {
	// Create an if expression: (if (< 5 3) (+ 1 2) (- 1 2))
	input := "(if (< 5 3) (+ 1 2) (- 1 2))"

	// Parse the expression
	l := lex.NewLexer(input)
	p := par.NewParser(l)
	expr, err := p.ParseExpression()
	if err != nil {
		t.Fatalf("Parse error: %s", err)
	}

	// Create environment and register built-in procedures
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)

	// Evaluate the expression
	result := Eval(expr, env)

	// Check if the result is an Integer object with value -1
	integer, ok := result.(*obj.Integer)
	if !ok {
		t.Fatalf("Eval didn't return Integer. Got %T", result)
	}

	if integer.Value != -1 {
		t.Errorf("Eval returned wrong value. Got %d, want -1", integer.Value)
	}
}

// TestIfWithTruthyValue tests if expressions with a truthy value (not #f)
func TestIfWithTruthyValue(t *testing.T) {
	// Create an if expression: (if 1 2 3) - since 1 is not #f, it should be truthy
	input := "(if 1 2 3)"

	// Parse the expression
	l := lex.NewLexer(input)
	p := par.NewParser(l)
	expr, err := p.ParseExpression()
	if err != nil {
		t.Fatalf("Parse error: %s", err)
	}

	// Create environment and register built-in procedures
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)

	// Evaluate the expression
	result := Eval(expr, env)

	// Check if the result is an Integer object with value 2
	integer, ok := result.(*obj.Integer)
	if !ok {
		t.Fatalf("Eval didn't return Integer. Got %T", result)
	}

	if integer.Value != 2 {
		t.Errorf("Eval returned wrong value. Got %d, want 2", integer.Value)
	}
}